package org.opens.lambda.stream;

import org.junit.Before;
import org.junit.Test;
import org.opens.lambda.entity.Trader;
import org.opens.lambda.entity.Transaction;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class StreamCombinedTraining {

    List<Transaction> transactions = null;

    Trader m1 = new Trader("m1", "city1");
    Trader m2 = new Trader("m2", "city2");
    Trader m3 = new Trader("m3", "city3");

    @Before
    public void init() {
        transactions = Arrays.asList(
                new Transaction(m1, 2013, 100),
                new Transaction(m2, 2011, 1300),
                new Transaction(m1, 2017, 400),
                new Transaction(m3, 2011, 300),
                new Transaction(m3, 2012, 350),
                new Transaction(m1, 2011, 900)
        );
    }

    /**
     * 功能:
     *      删选出所有订单记录中年份在2011年并且按照交易额进行排序(默认升序).
     * 输出:
     *      [Transaction{trader=Trader{name='m3', city='city3'}, year=2011, value=300}, Transaction{trader=Trader{name='m1', city='city1'}, year=2011, value=900}, Transaction{trader=Trader{name='m2', city='city2'}, year=2011, value=1300}]
     */
    @Test
    public void filterYearAndSortValue() {
        List<Transaction> result = transactions.stream().filter(x -> x.getYear() == 2011).sorted(Comparator.comparing(Transaction::getValue)).collect(Collectors.toList());
        System.out.println(result);
    }

    /**
     * 功能:
     *      筛选出所有订单商人的城市, 然后去重.
     * 输出:
     *      [city1, city2, city3]
     */
    @Test
    public void MapAndDistinct() {
        List<String> result = transactions.stream().map(x -> x.getTrader().getCity()).distinct().collect(Collectors.toList());
        System.out.println(result);
    }

    /**
     * 功能:
     *      取出所有订单的商人, 并过滤出所有城市在city1这个城市的商人, 最后按照商人的姓名进行排序.
     * 输出:
     *      [Trader{name='m1', city='city1'}, Trader{name='m1', city='city1'}, Trader{name='m1', city='city1'}]
     */
    @Test
    public void mapAndFilterAndSort() {
        List<Trader> result = transactions.stream().map(Transaction::getTrader).filter(x -> "city1".equals(x.getCity())).sorted(Comparator.comparing(Trader::getName)).collect(Collectors.toList());
        System.out.println(result);
    }

    /**
     * 功能:
     *      将订单中商人的姓名提取出来, 并聚合为一个字符串(按照字母排序).
     * 输出:
     *      m1m2m3
     * 说明:
     *      1. reduce的行为较难理解, 但是官方注释中给了一个很好的说明:
     *      <pre>
     *      {@code
     *          T result = identity;
     *          for (T element : this stream)
     *              result = accumulator.apply(result, element)
     *          return result;
     *      }
     *      </pre>
     *          - 其实就相当于给定一个初始值, 然后循环遍历这个流, 将结果按照传入的lambda表达式进行聚合.
     *          - 在下面的例子中, 再结合官方说明, 可以发现, name1的初始值就是给定的identity.
     */
    @Test
    public void mapAndDistinctAndSortAndReduce() {
        String reduce = transactions.stream().map(x -> x.getTrader().getName()).distinct().sorted().reduce("", (name1, name2) -> name1 + name2);
        System.out.println(reduce);
    }

    /**
     * 功能:
     *      判断订单的商人中是否有人在某个city.
     * 输出:
     *      true
     *      false
     * 说明:
     *      1. anyMatch只要有一个满足就返回true.
     */
    @Test
    public void antMatchExample() {
        boolean result1 = transactions.stream().anyMatch(x -> "city1".equals(x.getTrader().getCity()));
        boolean result2 = transactions.stream().anyMatch(x -> "city11".equals(x.getTrader().getCity()));
        System.out.println(result1);
        System.out.println(result2);
    }

    /**
     * 功能:
     *      将符合要求的数据筛选出来并生成固定的格式.
     * 输出:
     *      [city1-> 100, city1-> 400, city1-> 900]
     */
    @Test
    public void filterAndMapAndPolymerization() {
        List<String> collect = transactions.stream().filter(x -> "city1".equals(x.getTrader().getCity())).map(y -> y.getTrader().getCity() + "-> " + y.getValue()).collect(Collectors.toList());
        System.out.println(collect);
    }

    /**
     * 功能:
     *      使用reduce来获取出最大的交易额度.
     * 输出:
     *      1300
     *      100
     */
    @Test
    public void userReduceToGetMaxOrMin() {
        Optional<Integer> reduce = transactions.stream().map(Transaction::getValue).reduce(Integer::max);
        System.out.println(reduce.orElse(-1));
        Optional<Integer> reduce2 = transactions.stream().map(Transaction::getValue).reduce(Integer::min);
        System.out.println(reduce2.orElse(-1));
    }



}
